home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 41 / Amiga Format CD41 (1999-06)(Future Publishing)(GB)[!][issue 1999-07].iso / -seriously_amiga- / programming / other / gtlayout / source / ltp_layoutmenu.c < prev    next >
C/C++ Source or Header  |  1999-04-19  |  10KB  |  452 lines

  1. /*
  2. **    GadTools layout toolkit
  3. **
  4. **    Copyright © 1993-1998 by Olaf `Olsen' Barthel
  5. **        Freely distributable.
  6. **
  7. **    :ts=4
  8. */
  9.  
  10. #ifndef _GTLAYOUT_GLOBAL_H
  11. #include "gtlayout_global.h"
  12. #endif
  13.  
  14. #include "Assert.h"
  15.  
  16. #ifdef DO_MENUS    /* Support code */
  17.  
  18.     /* LTP_LayoutMenu(RootMenu *Root,WORD ExtraFront,WORD ExtraSpace):
  19.      *
  20.      *    Layout the menus, menu items and submenu items.
  21.      */
  22.  
  23. BOOL
  24. LTP_LayoutMenu(RootMenu *Root,LONG ExtraFront,LONG ExtraSpace)
  25. {
  26.     LONG        MenuLeft,
  27.                 ItemTop = 0,
  28.                 ItemWidth = 0,
  29.                 ItemShift = 0,
  30.                 CommandWidth = 0,
  31.                 Width;
  32.  
  33.     MenuNode    *Menu;
  34.     ItemNode    *Item,*FirstItem = NULL,*LastItem = NULL;
  35.  
  36.     BOOL        Correction;
  37.  
  38.         // That one's simple
  39.  
  40.     for(MenuLeft = ExtraFront, Menu = (MenuNode *)Root->MenuList.mlh_Head ; Menu->Node.mln_Succ ; Menu = (MenuNode *)Menu->Node.mln_Succ)
  41.     {
  42.         Menu->Menu.LeftEdge = MenuLeft;
  43.  
  44.         MenuLeft += Menu->Menu.Width + ExtraSpace;
  45.     }
  46.  
  47.         // Now run down the list of items
  48.  
  49.     Menu = (MenuNode *)Root->MenuList.mlh_Head;
  50.     for(Item = (ItemNode *)Root->ItemList.mlh_Head ; Item->Node.mln_Succ ; Item = (ItemNode *)Item->Node.mln_Succ)
  51.     {
  52.             // Hit a submenu item?
  53.  
  54.         if(Item->Flags & ITEMF_FirstSub)
  55.         {
  56.             ItemNode *    FirstSub;
  57.             ItemNode *    LastSub = NULL;
  58.             ItemNode *    Sub;
  59.             LONG        SubTop            = 0,
  60.                         SubWidth        = 0;
  61.             LONG        CommandWidth    = 0;
  62.  
  63.                 // This is where we started
  64.  
  65.             FirstSub = Item;
  66.  
  67.                 // Now determine the widest entry and
  68.                 // line them up below one another
  69.  
  70.             for(Sub = FirstSub ; Sub->Node.mln_Succ && (Sub->Flags & ITEMF_IsSub) ; Sub = (ItemNode *)Sub->Node.mln_Succ)
  71.             {
  72.                 LastSub = Sub;
  73.  
  74.                 if(Sub->Item.Width > SubWidth)
  75.                     SubWidth = Sub->Item.Width;
  76.  
  77.                 if((Width = LTP_GetCommandWidth(Root,Sub)) > CommandWidth)
  78.                     CommandWidth = Width;
  79.  
  80.                 Sub->Item.TopEdge = SubTop;
  81.  
  82.                 SubTop += Sub->Item.Height;
  83.             }
  84.  
  85.             SubWidth += 4 + CommandWidth;
  86.  
  87.                 // In the second pass, make all entries
  88.                 // use the same width
  89.  
  90.             for(Sub = FirstSub ; ; Sub = (ItemNode *)Sub->Node.mln_Succ)
  91.             {
  92.                     // Indent the entries that need it
  93.  
  94.                 if(Sub->Item.Flags & CHECKIT)
  95.                     ((struct IntuiText *)Sub->Item.ItemFill)->LeftEdge += 2 + Root->CheckWidth;
  96.  
  97.                 Sub->Item.Width = SubWidth;
  98.  
  99.                     // Adapt the separator bar size
  100.  
  101.                 if(Sub->Flags & ITEMF_IsBar)
  102.                     ((struct Image *)Sub->Item.ItemFill)->Width = Sub->Item.Width - 4;
  103.                 else
  104.                 {
  105.                         // Take care of command keys
  106.  
  107.                     if(Sub->Flags & ITEMF_Command)
  108.                     {
  109.                         struct IntuiText *Command = ((struct IntuiText *)Sub->Item.ItemFill)->NextText;
  110.  
  111.                         Command->LeftEdge = Sub->Item.Width - (IntuiTextLength(Command) + 2);
  112.                     }
  113.                 }
  114.  
  115.                     // In the next iteration, continue after the last
  116.                     // submenu item
  117.  
  118.                 if(Sub == LastSub)
  119.                 {
  120.                     Item = Sub;
  121.  
  122.                     break;
  123.                 }
  124.             }
  125.  
  126.             DB(kprintf("last sub |%s|\n",((struct IntuiText *)Item->Item.ItemFill)->IText));
  127.         }
  128.         else
  129.         {
  130.                 // Start a new menu?
  131.  
  132.             if(!FirstItem)
  133.             {
  134.                     // This is where we started
  135.  
  136.                 FirstItem = Item;
  137.  
  138.                     // Reset the data
  139.  
  140.                 ItemTop = ItemWidth = ItemShift = CommandWidth = 0;
  141.             }
  142.  
  143.                 // Line up the entries in a column
  144.  
  145.             Item->Item.TopEdge = ItemTop;
  146.  
  147.             ItemTop += Item->Item.Height;
  148.  
  149.                 // Search for the widest entry
  150.  
  151.             if(Item->Item.Width > ItemWidth)
  152.                 ItemWidth = Item->Item.Width;
  153.  
  154.                 // If the menu title is wider than the
  155.                 // items are, adapt the item width
  156.                 // accordingly.
  157.  
  158.             if(ItemWidth < Menu->Menu.Width - 8)
  159.                 ItemWidth = Menu->Menu.Width - 8;
  160.  
  161.                 // Search for the widest command sequence
  162.  
  163.             if((Width = LTP_GetCommandWidth(Root,Item)) > CommandWidth)
  164.                 CommandWidth = Width;
  165.  
  166.                 // This is for submenu items which will get
  167.                 // indented by this amount
  168.  
  169.             if(Item->Item.Width > ItemShift)
  170.                 ItemShift = Item->Item.Width;
  171.         }
  172.  
  173.             // Is this the last item for this menu?
  174.  
  175.         if(Item->Flags & ITEMF_LastItem)
  176.         {
  177.             ItemWidth += 4 + CommandWidth;
  178.  
  179.                 // Restart and layout all the items in this menu
  180.  
  181.             for(Item = FirstItem ; Item->Node.mln_Succ ; Item = (ItemNode *)Item->Node.mln_Succ)
  182.             {
  183.                     // Indent submenu items
  184.  
  185.                 if(Item->Flags & ITEMF_IsSub)
  186.                     Item->Item.LeftEdge += ItemShift + 6;
  187.                 else
  188.                 {
  189.                         // Indent items as necessary
  190.  
  191.                     if(Item->Item.Flags & CHECKIT)
  192.                         ((struct IntuiText *)Item->Item.ItemFill)->LeftEdge += 2 + Root->CheckWidth;
  193.  
  194.                     Item->Item.Width = ItemWidth;
  195.  
  196.                         // Adapt the separator bar size
  197.  
  198.                     if(Item->Flags & ITEMF_IsBar)
  199.                         ((struct Image *)Item->Item.ItemFill)->Width = Item->Item.Width - 4;
  200.                     else
  201.                     {
  202.                             // Take care of submenu item indicators and
  203.                             // command sequences
  204.  
  205.                         if(Item->Flags & (ITEMF_Command | ITEMF_HasSub))
  206.                         {
  207.                             struct IntuiText *Command = ((struct IntuiText *)Item->Item.ItemFill)->NextText;
  208.  
  209.                             Command->LeftEdge = Item->Item.Width - (IntuiTextLength(Command) + 2);
  210.                         }
  211.                     }
  212.                 }
  213.  
  214.                     // Abort if this is the last item for this menu
  215.  
  216.                 if(Item->Flags & ITEMF_LastItem)
  217.                     break;
  218.             }
  219.  
  220.             DB(kprintf("last item |%s|\n",((struct IntuiText *)Item->Item.ItemFill)->IText));
  221.  
  222.                 // The next iteration will start a new menu
  223.  
  224.             FirstItem = NULL;
  225.  
  226.             Menu = (MenuNode *)Menu->Node.mln_Succ;
  227.         }
  228.     }
  229.  
  230.         // Calculate the effective positions
  231.  
  232.     LTP_AdjustMenuPosition(Root);
  233.  
  234.     Correction = FALSE;
  235.  
  236.         // We start by chopping down the menus
  237.  
  238.     for(Item = (ItemNode *)Root->ItemList.mlh_Head ; Item->Node.mln_Succ ; Item = (ItemNode *)Item->Node.mln_Succ)
  239.     {
  240.             // That's where we start
  241.  
  242.         if(!FirstItem)
  243.             FirstItem = Item;
  244.  
  245.             // Skip submenu items
  246.  
  247.         if(!(Item->Flags & ITEMF_IsSub))
  248.             LastItem = Item;
  249.  
  250.             // Did we reach the end of the menu?
  251.  
  252.         if(Item->Flags & ITEMF_LastItem)
  253.         {
  254.             DB(kprintf("chopping |%s|->|%s|\n",((struct IntuiText *)FirstItem->Item.ItemFill)->IText,((struct IntuiText *)LastItem->Item.ItemFill)->IText));
  255.  
  256.                 // Chop down the menus
  257.  
  258.             Correction |= LTP_CorrectItemList(Root,FirstItem,LastItem);
  259.  
  260.             FirstItem = NULL;
  261.         }
  262.     }
  263.  
  264.     if(Correction)
  265.     {
  266.         Correction = FALSE;
  267.  
  268.             // Recalculate the positions
  269.  
  270.         LTP_AdjustMenuPosition(Root);
  271.     }
  272.  
  273.         // Next we shift the menus around to make them fit
  274.  
  275.     for(Menu = (MenuNode *)Root->MenuList.mlh_Head ; Menu->Node.mln_Succ ; Menu = (MenuNode *)Menu->Node.mln_Succ)
  276.     {
  277.         MenuLeft = Menu->Menu.LeftEdge + 4 + Menu->Width + 4;
  278.  
  279.             // Does it cross the right screen border?
  280.  
  281.         if(MenuLeft > Root->Screen->Width)
  282.         {
  283.                 // Is the menu wider than the screen?
  284.  
  285.             if(4 + Menu->Width + 4 > Root->Screen->Width)
  286.             {
  287.                 DB(kprintf("menu too wide\n"));
  288.                 return(FALSE);
  289.             }
  290.             else
  291.             {
  292.                 LONG Left = MenuLeft - Root->Screen->Width + 1;
  293.  
  294.                     // Move up
  295.  
  296.                 for(Item = (ItemNode *)((ULONG)Menu->Menu.FirstItem - sizeof(struct MinNode)) ; Item->Node.mln_Succ ; Item = (ItemNode *)Item->Node.mln_Succ)
  297.                 {
  298.                     if(!(Item->Flags & ITEMF_IsSub))
  299.                         Item->Item.LeftEdge -= Left;
  300.  
  301.                     if(Item->Flags & ITEMF_LastItem)
  302.                         break;
  303.                 }
  304.  
  305.                 Correction = TRUE;
  306.             }
  307.         }
  308.     }
  309.  
  310.     if(Correction)
  311.     {
  312.         Correction = FALSE;
  313.  
  314.             // Recalculate the positions
  315.  
  316.         LTP_AdjustMenuPosition(Root);
  317.     }
  318.  
  319.         // Now deal with the submenus
  320.  
  321.     for(Item = (ItemNode *)Root->ItemList.mlh_Head ; Item->Node.mln_Succ ; Item = (ItemNode *)Item->Node.mln_Succ)
  322.     {
  323.             // Only submenu items, please
  324.  
  325.         if(Item->Flags & ITEMF_FirstSub)
  326.         {
  327.             ItemNode *Here;
  328.  
  329.                 // Find the first and the last entry
  330.  
  331.             for(Here = Item ; Here->Node.mln_Succ ; Here = (ItemNode *)Here->Node.mln_Succ)
  332.             {
  333.                 if(!(Here->Flags & ITEMF_IsSub))
  334.                     break;
  335.                 else
  336.                     LastItem = Here;
  337.             }
  338.  
  339.             DB(kprintf("2) chopping |%s|->|%s|\n",((struct IntuiText *)Item->Item.ItemFill)->IText,((struct IntuiText *)LastItem->Item.ItemFill)->IText));
  340.  
  341.                 // Chop down the submenus
  342.  
  343.             Correction |= LTP_CorrectItemList(Root,Item,LastItem);
  344.         }
  345.     }
  346.  
  347.     if(Correction)
  348.     {
  349.         Correction = FALSE;
  350.  
  351.         LTP_AdjustMenuPosition(Root);
  352.     }
  353.  
  354.         // Almost finished, now shift the submenus around
  355.  
  356.     for(Item = (ItemNode *)Root->ItemList.mlh_Head ; Item->Node.mln_Succ ; Item = (ItemNode *)Item->Node.mln_Succ)
  357.     {
  358.             // Did we hit a submenu?
  359.  
  360.         if(Item->Flags & ITEMF_HasSub)
  361.         {
  362.             ItemNode *Here = (ItemNode *)((ULONG)Item->Item.SubItem - sizeof(struct MinNode));
  363.  
  364.                 // Does this one also cross the right screen border?
  365.  
  366.             if((MenuLeft = Here->Left + 4 + Here->Item.Width + 4) > Root->Screen->Width)
  367.             {
  368.                 LONG Left = MenuLeft - Root->Screen->Width;
  369.  
  370.                     // Check if the subitem would cover too much
  371.                     // of the item it is attached to
  372.  
  373.                 if(Here->Left - Left < (Item->Left + 4 + Item->Width - CommandWidth))
  374.                 {
  375.                     Left = Here->Left + (4 + Here->Item.Width + 4) - Item->Left - 5;
  376.  
  377.                     if(Here->Left - Left < 0)
  378.                     {
  379.                         DB(kprintf("sub too fat\n"));
  380.                         return(FALSE);
  381.                     }
  382.                 }
  383.  
  384.                 DB(kprintf("3) shifting...\n"));
  385.  
  386.                     // Shift the menu around
  387.  
  388.                 do
  389.                 {
  390.                     if(!(Here->Flags & ITEMF_IsSub))
  391.                         break;
  392.                     else
  393.                     {
  394.                         Here->Item.LeftEdge -= Left;
  395.                         Here->Left -= Left;
  396.  
  397.                         Correction = TRUE;
  398.  
  399.                         if(Here->Flags & ITEMF_LastItem)
  400.                             break;
  401.  
  402.                         Here = (ItemNode *)Here->Node.mln_Succ;
  403.                     }
  404.                 }
  405.                 while(Here->Node.mln_Succ);
  406.             }
  407.         }
  408.     }
  409.  
  410.         // The last step; see if the alignment stuff worked
  411.  
  412.     if(Correction)
  413.         LTP_AdjustMenuPosition(Root);
  414.  
  415.         // First check the menus
  416.  
  417.     for(Menu = (MenuNode *)Root->MenuList.mlh_Head ; Menu->Node.mln_Succ ; Menu = (MenuNode *)Menu->Node.mln_Succ)
  418.     {
  419.             // Does it cross the screen borders?
  420.  
  421.         if((MenuLeft = Menu->Menu.LeftEdge + 4 + Menu->Width + 4) > Root->Screen->Width || Menu->Menu.LeftEdge < 0)
  422.         {
  423.             DB(kprintf("menu crosses screen border\n"));
  424.             return(FALSE);
  425.         }
  426.     }
  427.  
  428.         // Now check the submenus
  429.  
  430.     for(Item = (ItemNode *)Root->ItemList.mlh_Head ; Item->Node.mln_Succ ; Item = (ItemNode *)Item->Node.mln_Succ)
  431.     {
  432.             // Did we hit a submenu?
  433.  
  434.         if(Item->Flags & ITEMF_HasSub)
  435.         {
  436.             ItemNode *Here = (ItemNode *)((ULONG)Item->Item.SubItem - sizeof(struct MinNode));
  437.  
  438.             if(Here->Left + Here->Width > Root->Screen->Width)
  439.             {
  440.                 DB(kprintf("submenu crosses screen border\n"));
  441.                 return(FALSE);
  442.             }
  443.         }
  444.     }
  445.  
  446.     LTP_FillMenu(&Root->Menu);
  447.  
  448.     return(TRUE);
  449. }
  450.  
  451. #endif    /* DO_MENUS */
  452.